#pragma once

#include <iostream>
#include <functional>
#include <vector>
#include <string>
#include <unordered_map>
#include <cassert>
#include <cerrno>
#include "Log.hpp"
#include "Sock.hpp"
#include "Epoll.hpp"
#include "Protocol.hpp"

class TcpServer;
class Connection;

using func_t = std::function<void(Connection *)>;
using callback_t = std::function<void(Connection *, std::string &)>;

// 使用 Connection 来表示一个连接
// 该连接有自己的接收缓冲区和发送缓冲区
class Connection
{
public:
    Connection(int sock = -1)
        : _sock(sock), _ptr(nullptr)
    {
    }

    // 设置该连接的回调函数
    void SetCallBack(func_t readCallBack, func_t writeCallBack, func_t exceptCallBack)
    {
        _readCallBack = readCallBack;
        _writeCallBack = writeCallBack;
        _exceptCallBack = exceptCallBack;
    }

    ~Connection()
    {
        if (_sock >= 0)
            close(_sock);
    }

public:
    // 负责进行 IO 的文件描述符
    int _sock;
    // 三个回调方法,表示对 _sock 的读事件、写事件和异常事件的处理方法
    func_t _readCallBack;
    func_t _writeCallBack;
    func_t _exceptCallBack;
    // 每个连接独占的接收缓冲区和发送缓冲区
    std::string _inBuffer; // 暂时没有办法处理二进制流，文本数据是可以处理的
    std::string _outBuffer;
    // TcpServer 的回指指针,用于业务处理
    TcpServer *_ptr;
};

class TcpServer
{
private:
    const static int defaultPort = 8080;
    const static int defaultMaxEvents = 128;

public:
    TcpServer(int port = defaultPort, int maxEvents = defaultMaxEvents)
        : _port(port), _maxEvents(maxEvents)
    {
        // 1. 创建监听套接字
        _listenSock = Sock::Socket();
        Sock::Bind(_listenSock, _port);
        Sock::Listen(_listenSock);
        // 2. 创建 epoll 模型
        _epoll.CreateEpoll();
        // 3. 将 _listenSock 封装成 Connection 添加到 TcpServer 中
        // _listenSock 只需要关心读事件即可
        AddConnection(_listenSock, std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        // 4. 申请保存就绪事件的数组
        _readyEvents = new struct epoll_event[_maxEvents];
        logMessage(NORMAL, "Init TcpServer Success");
    }

    ~TcpServer()
    {
        if (_listenSock >= 0)
            close(_listenSock);
        if (_readyEvents)
            delete[] _readyEvents;
    }

    // 根据就绪的事件进行特定事件的派发
    void Dispacther(callback_t callBack)
    {
        _callBack = callBack;
        while (true)
        {
            LoopOnce();
        }
    }

    void EnableReadWrite(Connection *conn, bool readAble, bool writeAble)
    {
        uint32_t events = ((readAble ? EPOLLIN : 0) | (writeAble ? EPOLLOUT : 0));
        bool ret = _epoll.CtlEpoll(conn->_sock, events);
        assert(ret);
    }

private:
    // 将连接添加到 TcpServer 中
    void AddConnection(int sock, func_t readCallBack, func_t writeCallBack, func_t exceptCallBack)
    {
        /* ************************************************************
         * TcpServer 上将会存在大量的连接,也就是会存在大量的 Connection
         * 那么 TcpServer 就需要采用先描述再组织的方式来将大量的 Connection
         * 管理起来，在这里我是要哈希表的方式来管理
         * ************************************************************
         */

        // 1. 将文件描述符设置为非阻塞（ET模式）
        Sock::SetNonBlock(sock);
        // 2. 构建 Connection 对象
        Connection *conn = new Connection(sock);
        conn->SetCallBack(readCallBack, writeCallBack, exceptCallBack);
        conn->_ptr = this;
        // 3. 将 sock 添加到 epoll 中
        // 任何的多路转接服务器一般默认只会打开对
        // 读事件的关心，对写事件的关心按需打开
        _epoll.AddSockToEpoll(sock, EPOLLIN | EPOLLET);
        // 4. 保存文件描述符与 Connection 的映射关系
        _connections[sock] = conn;
    }

    void LoopOnce()
    {
        int n = _epoll.WaitEpoll(_readyEvents, _maxEvents);
        for (int i = 0; i < n; ++i)
        {
            int sock = _readyEvents[i].data.fd;        // 就绪的文件描述符
            uint32_t revents = _readyEvents[i].events; // 就绪的事件

            // 将所有的异常都交给Recver或Sender来处理
            if (revents & EPOLLERR)
                revents |= (EPOLLIN | EPOLLOUT);
            if (revents & EPOLLHUP)
                revents |= (EPOLLIN | EPOLLOUT);

            // 如果连接存在且设置了相应的回调方法，才可以调用回调方法
            if (revents & EPOLLIN)
            {
                if (ConnectionIsExists(sock) && _connections[sock]->_readCallBack != nullptr)
                    _connections[sock]->_readCallBack(_connections[sock]);
            }
            if (revents & EPOLLOUT)
            {
                if (ConnectionIsExists(sock) && _connections[sock]->_writeCallBack != nullptr)
                    _connections[sock]->_writeCallBack(_connections[sock]);
            }
        }
    }

    // _listenSock 用来接收新连接的回调方法
    void Accepter(Connection *conn)
    {
        // 来到这里说明有新的连接到来,因为不知道底层有多少个连
        // 接,所以需要通过while循环一直获取连接。如果不是这样
        // 的话,可能会导致有些客户端长时间连接不上服务器的问题
        while (true)
        {
            std::string clientIp;
            uint16_t clientPort;
            int acceptErrno = 0;
            int sock = Sock::Accept(_listenSock, &clientIp, &clientPort, &acceptErrno);
            if (sock < 0)
            {
                if (acceptErrno == EAGAIN || acceptErrno == EWOULDBLOCK)
                    break; // 底层的连接已经全部获取完了,可以退出循环了
                else if (acceptErrno == EINTR)

                    continue; // 被信号中断,可以继续获取连接
                else
                {
                    // accept失败
                    logMessage(WARNING, "Accepter Error:%d %s", acceptErrno, strerror(acceptErrno));
                    break;
                }
            }
            // 将 sock 添加到 TcpServer 中
            if (sock >= 0)
            {
                AddConnection(sock, std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                             std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                             std::bind(&TcpServer::Excepter, this, std::placeholders::_1));
                logMessage(DEBUG, "Accept Client[%s:%d:%d] Success", clientIp.c_str(), clientPort, sock);
            }
        }
    }

    // 普通套接字读事件就绪的回调方法
    void Recver(Connection *conn)
    {
        const int num = 1024;
        while (true)
        {
            char buffer[num];
            ssize_t n = recv(conn->_sock, buffer, sizeof(buffer) - 1, 0);
            if (n < 0)
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else // 出现错误
                {
                    logMessage(ERROR, "Recver Error:%d %s", errno, strerror(errno));
                    conn->_exceptCallBack(conn);
                    return; // 直接返回
                }
            }
            else if (n == 0) // 对端关闭连接
            {
                logMessage(DEBUG, "Client[%d] Quit, Me Too", conn->_sock);
                conn->_exceptCallBack(conn);
                return; // 直接返回
            }
            else
            {
                // 读取底层数据成功
                buffer[n] = 0;
                conn->_inBuffer += buffer;
            }
        }

        // 打印获取到的数据
        logMessage(DEBUG, "Client[%d] Send Message:%s", conn->_sock, conn->_inBuffer.c_str());
        std::vector<std::string> messages;
        SpliteMessage(conn->_inBuffer, messages);
        // 将分割出来的独立报文交给具体业务进行处理
        // 在这里可以将message封装成为task,然后push
        // 到任务队列,任务处理交给后端线程池
        for (auto &msg : messages)
            _callBack(conn, msg); // msg 就是一个完整的报文
    }

    void Sender(Connection *conn)
    {
        while (true)
        {
            ssize_t n = send(conn->_sock, conn->_outBuffer.c_str(), conn->_outBuffer.size(), 0);
            if (n > 0)
            {
                conn->_outBuffer.erase(0, n);
                if (conn->_outBuffer.empty())
                    break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK) // 底层TCP的发送缓冲区没有空间了
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    logMessage(ERROR, "Sender Error:%d %s", errno, strerror(errno));
                    conn->_exceptCallBack(conn);
                    return;
                }
            }
        }

        // 如果数据发送完了,则需要取消对conn->_sock写事件的关心
        // 如果数据没有发送完,则不需要取消对conn->_sock写事件的关心
        if (conn->_outBuffer.empty())
            EnableReadWrite(conn, true, false);
        else
            EnableReadWrite(conn, true, true);
    }

    void Excepter(Connection *conn)
    {
        if (!ConnectionIsExists(conn->_sock))
            return;
        // 1. 从epoll中移除conn->_sock
        int n = _epoll.DelSockFromEpoll(conn->_sock);
        assert(n);
        (void)n;
        // 2. 从_connections中移除conn->_sock
        _connections.erase(conn->_sock);
        // 3. 释放conn所占用的资源(Connect的析构函数会关闭文件描述符)
        delete conn;
    }

    // 检查连接是否存在
    bool ConnectionIsExists(int sock)
    {
        return _connections.count(sock) == 1;
    }

private:
    int _listenSock;
    int _port;
    Epoll _epoll;
    std::unordered_map<int, Connection *> _connections;
    struct epoll_event *_readyEvents;
    int _maxEvents;
    // 业务处理的回调函数
    callback_t _callBack;
};